package name;

import java.io.*;
import java.util.*;

/**
 * Allows the generation of random names from one or more text files.  Names in the text file can be complete names or just phonetics that will be joined together.  Comment lines in text files begin with '//'.  The length of generated names (how many phonetics each name is made up of) can be customized to fit either method and may be randomly selected.  If more than one file is specified, the first phonetic will come from the first file, the second phonetic from the second file, etc.  If there are more phonetics than files, the extra phonetics will come from the last file.  If there are more files than phonetics, only the necessary files will be used.  Names can be run through any number of NameBuilderMethods as well to modify the way phonetics are assembled.  The order in which a name passes through several methods may affect its final representation.
 *
 * @author Catastrophe
 */
public final class NameBuilder
{
	/**
	 * The list of phonetics to choose from.
	 */
	private List<List<String>> nameList;

	/**
	 * The desired average length of names (in phonetics).
	 */
	private double averageLength;

	/**
	 * The maximum allowable deviation from the average length.
	 */
	private double maxDeviation;

	/**
	 * Collection of methods this NameBuilder uses to combine phonetics.
	 */
	private Collection<NameBuilderMethod> methods = new ArrayList<NameBuilderMethod>();

	/**
	 * Creates a new NameBuilder.
	 *
	 * @param nameFiles a List of the Files containing the list of phonetics or names to use in order of use
	 * @param averageLength the desired average length of generated names
	 * @param maxDeviation the maximum desired variation from the average length
	 * @throws FileNotFoundException if <i>nameFile</i> does not exist
	 * @throws IllegalArgumentException if <i>averageLength</i> or <i>maxDeviation</i> < 0
	 */
	public NameBuilder(List<File> nameFiles, double averageLength, double maxDeviation) throws FileNotFoundException, IllegalArgumentException
	{
		if(averageLength < 0 || maxDeviation < 0)
			throw new IllegalArgumentException();
		this.nameList = new ArrayList<List<String>>();
		for(int i = 0; i < nameFiles.size(); ++i)
			addNameList(nameFiles.get(i));
		this.averageLength = averageLength;
		this.maxDeviation = maxDeviation;
	}

	/**
	 * Creates a new NameBuilder.
	 *
	 * @param nameFiles a List of the Files containing the list of phonetics or names to use in order of use
	 * @param minimumSize the minimum desired name size
	 * @param maximumSize the maximum desired name size
	 * @throws FileNotFoundException if <i>nameFile</i> does not exist
	 * @throws IllegalArgumentException if <i>minimumSize</i> < 0 or <i>minimumSize</i> > <i>maximumSize</i>
	 */
	public NameBuilder(List<File> nameFiles, int minimumSize, int maximumSize) throws FileNotFoundException, IllegalArgumentException
	{
		this(nameFiles, (double)(minimumSize + maximumSize) / 2, (double)(maximumSize - minimumSize) / 2);
	}

	/**
	 * Creates a new NameBuilder.
	 *
	 * @param nameFiles a List of the Files containing the list of phonetics or names to use in order of use
	 * @param size the desired name size (all names will be exactly this size)
	 * @throws FileNotFoundException if <i>nameFile</i> does not exist
	 * @throws IllegalArgumentException if <i>size</i> < 0
	 */
	public NameBuilder(List<File> nameFiles, int size) throws FileNotFoundException, IllegalArgumentException
	{
		this(nameFiles, size, size);
	}

	/**
	 * Makes names generated by this NameBuilder pass through another NameBuilderMethod.  The name will pass through all previously added methods before the newly added method.
	 *
	 * @param builderMethod the NameBulderMethod newly generated names must pass through
	 */
	public void addNameBuilderMethod(NameBuilderMethod builderMethod)
	{
		methods.add(builderMethod);
	}

	/**
	 * Removes the specified NameBuilderMethod from the list of methods that are applied to newly generated names.
	 *
	 * @param builderMethod the NameBuilderMethod to remove
	 */
	public void removeNameBuilderMethod(NameBuilderMethod builderMethod)
	{
		methods.remove(builderMethod);
	}

	/**
	 * Returns a new randomly generated name each time this method is called.
	 *
	 * @return a randomly generated name
	 */
	public String getName()
	{
		int length = (int)(averageLength + (Math.random() - 0.5) * 2 * maxDeviation);
		String [] phonetics = new String [length];

		for(int i = 0; i < length; ++i)
		{
			int j = i;
			if(j >= nameList.size())
				j = nameList.size() - 1;
			phonetics[i] = nameList.get(j).get((int)(Math.random() * nameList.get(j).size()));
		}

		for(NameBuilderMethod method : methods)
			method.buildName(phonetics);

		String name = new String();
		for(String phonetic : phonetics)
			name += phonetic;

		return name;
	}

	/**
	 * Sets up this NameBuilder's list of phonetics from the supplied file.
	 *
	 * @param nameFile the File containing the list of phonetics
	 * @throws FileNotFoundException if the specified File does not exist or is corrupt
	 */
	private void addNameList(File nameFile) throws FileNotFoundException
	{
		BufferedReader br = new BufferedReader(new FileReader(nameFile));
		try
		{
			String s;
			List<String> v = new ArrayList<String>();
			while((s = br.readLine()) != null)
				if(!s.startsWith("//"))
					v.add(s);
			nameList.add(v);
		}
		catch(IOException e)
		{
			throw new FileNotFoundException();
		}
	}

	/**
	 * Test program.
	 *
	 * @param args no effect.
	 */
	public static void main(String[] args)
	{
		try
		{
		Vector<File> names = new Vector<File>();
		names.add(new File("first.txt"));
		names.add(new File("last.txt"));
		NameBuilder nb = new NameBuilder(names, 2);
		nb.addNameBuilderMethod(new InterruptedNameBuilderMethod(1.0, new char[] {' '}));
		for(int i = 0; i < Integer.parseInt(args[0]); ++i)
		System.out.println(nb.getName());
		}
		catch(Exception e)
		{}
	}
}